/*
 * Merupakan kelas utama yang akan menangani seluruh operasi widget engine
 */
package org.iwidget.desktop.core;

import org.iwidget.desktop.model.*;
import org.iwidget.desktop.ui.*;
import org.iwidget.desktop.manager.*;
import cryptix.jce.provider.CryptixCrypto;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.URLEncoder;
import java.security.Provider;
import java.security.Security;
import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.swing.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
 *
 * @author Muhammad Hakim A
 */
public class Iwidget extends JDialog
        implements WindowListener, MouseListener, ActionListener, ItemListener//, KeyListener
{
    // garbage collecting

    class MyTimerTask extends TimerTask {

        public void run() {
            System.gc();
        }

        MyTimerTask() {
        }
    }

    class createWidgetThread extends Thread {

        @Override
        public void run() {
            String widgetName = title;
            if (widgetName.endsWith(".qw")) {
                widgetName = widgetName.substring(0, widgetName.length() - 3);
            }
            WidgetElement widget = createWidget(title, isStartUp);

            if (widget != null && widget.getWindowJS() != null) {
                widget.getWindowJS().setTitle(widgetName);
                debugFrame.addText("Widget loaded: " + title);
            }

            CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(title);
            if (mi2 != null) {
                mi2.setState(true);//mi2.setSelected(true);
            } else {
                synchronized (widgetMenu) {
                    CheckboxMenuItem mi = new CheckboxMenuItem();
                    //mi.setText(widgetName);
                    mi.setLabel(widgetName);
                    if (widget != null) {
                        mi.setState(true);//mi.setSelected(true);
                    }
                    mi.setActionCommand(":" + widgetName);
                    mi.addActionListener(parent);

                    mi.addItemListener(parent);
                    widgetMenu.add(mi);
                    widgetMenuList.put(widgetName, mi);
                }
            }
        }
        private String title;
        private Iwidget parent;
        private boolean isStartUp;

        public createWidgetThread(Iwidget parent, String title, boolean isStartUp) {
            this.parent = parent;
            this.title = title;
            this.isStartUp = isStartUp;
        }
    }

    // main
    public static void main(String args[])
            throws Exception {
        new Iwidget();
    }

    /**
     * get timer (java.util.Timer)
     */
    public java.util.Timer getTimer() {
        return timer;
    }

    /**
     * get DebugFrame, return reference ke DebugFrame
     */
    public DebugFrame getDebugFrame() {
        return debugFrame;
    }

    /**
     * get operating system name
     */
    public String getOSName() {
        return osName;
    }

    public String[] getWidgetsDir() {
        return widgetsDir;
    }

    /**
     * Iwidget ctor, initial operation (initialization variables)
     */
    private Iwidget() {
        isDragging = false;
        foundError = false;
        javaVersion = System.getProperty("java.version");
        iterationCount = 19;
        thisInstance = this;

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            System.out.println("Error setting native LAF: " + e);
        }

        osName = System.getProperty("os.name");
        Security.addProvider(new CryptixCrypto());
        // read preferences
        File f = new File("./prefs");
        f.mkdir();
        widgetList = new Hashtable();
        widgetMenuList = new Hashtable();
        widgetVersions = new Properties();
        widgetVersions.put("iWidget", "1.0.0");
        timer = new java.util.Timer();
        MyTimerTask myTimerTask = new MyTimerTask();
        timer.schedule(myTimerTask, 60000L, 60000L);
        initSecurity();
        // prepare debugframe
        debugFrame = new DebugFrame(widgetList);
        debugFrame.addText("iWidget v1.0.0");
        debugFrame.addText("Copyright \251 2008 by Hakim");
        debugFrame.addText("All Rights Reserved");
        debugFrame.addText("-----------------------------------------------------");

        new IwidgetRepository();
        loadConfiguration();
        createMenu();
        createAllWidgets();
        debugFrame.addText("Operating System: " + osName);
        if (osName.startsWith("Windows")) {
            initDesktop();
            Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
            int x = screen.width - 220;
            int y = screen.height - 30;
            setLocation(x, y);
            setSize(1, 1);
        } else {
            JLabel label = new JLabel(new ImageIcon("./images/iwicon.png"));
            label.setToolTipText("Right click to display iWidget menu");
            label.addMouseListener(this);

            getContentPane().add(label);
            setSize(40, 40);
        }
        //manager
        manager = new Manager(this);

        setUndecorated(true);
        setTitle("iWidget");
        setVisible(true);
        addWindowListener(this);
        iwidgetMouse = new IwidgetMouseLoop(this, widgetList);
        //debugFrame.setVisible(true);
    }

    private void loadConfiguration() {
        openWidgets = new Properties();
        try {
            openWidgets.load(new FileInputStream("./iWidget.cfg"));
        } catch (Exception e) {
            debugFrame.addText("SYSTEM:Error:Could not load iWidget configuration file");
        }
    }

    private void saveConfiguration() {
        try {
            openWidgets.store(new FileOutputStream("./iWidget.cfg"), "");
        } catch (Exception e) {
            debugFrame.addText("SYSTEM:Error:Could not save iWidget configuration file");
        }
    }

    private boolean initDesktop() {
        // setting system tray icon. mensyaratkan jre > jre 6      
        if (SystemTray.isSupported()) {
            systray = SystemTray.getSystemTray();
            Image icon = Toolkit.getDefaultToolkit().createImage("./images/iwicon.png");
            trayicon = new TrayIcon(icon, "iWidget", menu);

            trayicon.setImageAutoSize(true);

            try {
                systray.add(trayicon);
                return true;
            } catch (AWTException e) {
                System.err.println("TrayIcon could not be added.");
                return false;
            }

        } else {
            debugFrame.addText("SYSTEM:ERROR: System Tray is currently not supported");
            return false;
        }
    }

    /**
     * menus for system tray icon/desktop indicator
     */
    private void createMenu() {
        menu = new PopupMenu();
        MenuItem mi = menu.add(new MenuItem("About iWidget"));
        mi.setActionCommand("About");
        mi.addActionListener(this);
        menu.addSeparator();
        mi = menu.add(new MenuItem("Check for updates..."));
        mi.setActionCommand("Updates");
        mi.addActionListener(this);
        mi = menu.add(new MenuItem("Get more widgets..."));
        mi.setActionCommand("GetMore");
        mi.addActionListener(this);
        menu.addSeparator();
        widgetMenu = new Menu("Widgets");
        menu.add(widgetMenu);
        menu.addSeparator();
        mi = menu.add(new MenuItem("Debug"));
        mi.setActionCommand("Debug");
        mi.addActionListener(this);
        mi = menu.add(new MenuItem("Manager"));
        mi.setActionCommand("Manager");
        mi.addActionListener(this);
        menu.addSeparator();
        mi = menu.add(new MenuItem("Close iWidget"));
        mi.setActionCommand("Close");
        mi.addActionListener(this);
    }

    /**
     * menus for widgets
     */
    private void buildWidgetMenu() {
        File widgetFile = new File("./My Widgets/");
        String widgets[] = widgetFile.list();
        if (widgets != null) {
            for (int iCtr = 0; iCtr < widgets.length; iCtr++) {
                boolean found = false;
                for (int jCtr = 0; jCtr < widgetsDir.length && !found; jCtr++) {
                    if (!widgetsDir[jCtr].equals(".DS_Store") && widgetsDir[jCtr].equals(widgets[iCtr])) {
                        found = true;
                    }
                }

                if (!found) {
                    IwidgetRepository.getInstance().reinitialize();
                    createWidgetThread t = new createWidgetThread(this, widgets[iCtr], true);
                    t.start();
                }
            }

        }

        for (int jCtr = 0; jCtr < widgetsDir.length; jCtr++) {
            boolean found = false;
            for (int iCtr = 0; iCtr < widgets.length && !found; iCtr++) {
                if (widgets[iCtr].equals(widgetsDir[jCtr])) {
                    found = true;
                }
            }

            if (found) {
                continue;
            }
            String widgetName = widgetsDir[jCtr];
            if (widgetName.endsWith(".qw")) {
                widgetName = widgetName.substring(0, widgetName.length() - 3);
            }
            CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(widgetName);
            widgetMenuList.remove(mi2);
            widgetMenu.remove(mi2);
            closeWidget(widgetName);
            IwidgetRepository.getInstance().reinitialize();
        }

        widgetsDir = widgets;
    }

    /**
     * systray menus event handler
     */
    public void actionPerformed(ActionEvent e) {
        //MenuItem mi = (MenuItem)e.getSource();
        UpdateFrame frame;
        if (e.getActionCommand().equals("Close")) {
            saveConfiguration();
            for (Enumeration enum1 = widgetList.keys(); enum1.hasMoreElements(); closeWidget((String) enum1.nextElement()));
            systray.remove(trayicon);
            System.exit(0);
        } else if (e.getActionCommand().equals("About")) {
            AboutElement about = new AboutElement(null);
            AboutFrame aboutFrame = new AboutFrame(null, about);
        } else if (e.getActionCommand().equals("Debug")) {
            debugFrame.setVisible(true);
        } else if (e.getActionCommand().equals("Manager")) {
            manager.ConfigureButton(widgetList);
            manager.setVisible(true);
        } else if (e.getActionCommand().equals("AskSupport")) {
            try {
                BrowserLauncher.openURL("mailto:hakimrie@gmail.com");
            } catch (Exception ex) {
                debugFrame.addText("SYSTEM:Error:Unable to reach Iwidget support");
            }
        } else if (e.getActionCommand().equals("GetMore")) {
            try {
                BrowserLauncher.openURL("http://iwidget.googlecode.com/");
            } catch (Exception ex) {
                debugFrame.addText("SYSTEM:Error:Unable to reach iWidget widget gallery");
            }
        } else if (e.getActionCommand().equals("Updates")) {
            frame = new UpdateFrame(this, widgetVersions);
        } else if (e.getActionCommand().startsWith(":")) {
            String widgetName = e.getActionCommand().substring(1);
            WidgetElement widget = (WidgetElement) widgetList.get(widgetName);

            if (widget == null) {
                CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(widgetName);

                mi2.setState(true);
                try {
                    String encodedWidgetName = URLEncoder.encode(widgetName, "UTF-8");
                    openWidgets.put(encodedWidgetName, "1");
                } catch (Exception ex) {
                }

                // buat widget
                createWidgetThread t = new createWidgetThread(this, widgetName, false);
                t.start();
            } else if (widget.getUIComponent() != null) {
                CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(widgetName);
                //mi2.setSelected(true);
                mi2.setState(true);
                widget.getUIComponent().toFront();
            }
        }
    }

    public void itemStateChanged(ItemEvent e) {
        CheckboxMenuItem cbx = (CheckboxMenuItem) e.getSource();

        if (cbx.getActionCommand().startsWith(":")) {
            String widgetName = cbx.getActionCommand().substring(1);
            WidgetElement widget = (WidgetElement) widgetList.get(widgetName);

            if (widget == null) {
                CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(widgetName);

                mi2.setState(true);
                try {
                    String encodedWidgetName = URLEncoder.encode(widgetName, "UTF-8");
                    openWidgets.put(encodedWidgetName, "1");
                } catch (Exception ex) {
                }

                // buat widget
                createWidgetThread t = new createWidgetThread(this, widgetName, false);
                t.start();
            } else if (widget.getUIComponent() != null) {
                CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(widgetName);
                //mi2.setSelected(true);
                mi2.setState(true);
                //if (!mi2.getState()) widget.close();
                //else{
                //  createWidgetThread t = new createWidgetThread(this, widgetName, false);
                // t.start();
                //}
                widget.getUIComponent().toFront();
            }
        }
    }

    /**
     * create widgets from its definitions in library folder
     */
    private void createAllWidgets() {
        File widgetFile = new File("./My Widgets/");
        widgetsDir = widgetFile.list();
        if (widgetsDir != null) {
            for (int iCtr = 0; iCtr < widgetsDir.length; iCtr++) {
                if (!widgetsDir[iCtr].equals(".DS_Store")) {
                    createWidgetThread t = new createWidgetThread(this, widgetsDir[iCtr], true);
                    t.start();
                }
            }

        }
        buildWidgetMenu();
    }

    /**
     * create single widget from definition
     */
    private WidgetElement createWidget(String widgetName, boolean isStartUp) {
        if (widgetName.indexOf(".qw") > 0) {
            widgetName = widgetName.substring(0, widgetName.length() - 3);
        }
        WidgetElement result = constructWidget(widgetName, "widget.xml");
        if (result != null) {
            // versi widget
            widgetVersions.put(widgetName, result.getWidgetVersion());
            StringTokenizer tokens = new StringTokenizer(result.getIwidgetVersion(), ".");
            int srcMajor = 1;

            try {
                srcMajor = Integer.parseInt((String) tokens.nextElement());
            } catch (Exception e) {
            }

            int srcMinor = 0;
            try {
                srcMinor = Integer.parseInt((String) tokens.nextElement());
            } catch (Exception e) {
            }

            int srcBuild = 0;
            try {
                srcBuild = Integer.parseInt((String) tokens.nextElement());
            } catch (Exception e) {
            }

            boolean loadOK = true;
            if (srcMajor > 1 || srcMinor > 3 || srcBuild > 7) {
                loadOK = false;
            }

            String portability = result.getPortability();
            if (portability.equals("windows") && !osName.startsWith("Windows") || portability.equals("linux") && !osName.startsWith("Linux")) {
                loadOK = false;
            }

            String encodedWidgetName = null;
            try {
                encodedWidgetName = URLEncoder.encode(widgetName, "UTF-8");
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (loadOK) {
                String openStart = openWidgets.getProperty(encodedWidgetName, null);

                if (openStart == null && isStartUp && result.isLoadOnStartUp()) {
                    openStart = "1";
                }

                if (openStart != null && openStart.equals("1") || isStartUp && result != null && result.isLoadOnStartUp()) {
                    widgetList.put(widgetName, result);
                    result.widgetLoaded();
                    openWidgets.put(encodedWidgetName, "1");
                } else {
                    openWidgets.put(encodedWidgetName, "0");
                    result = null;
                }
            } else {
                openWidgets.put(encodedWidgetName, "0");
                StringBuffer sb2 = new StringBuffer();
                sb2.append("Error:Widget \"" + widgetName + "\" requires\n").append("               OS Type:" + result.getPortability() + "\n").append("      Iwidget Version:" + result.getIwidgetVersion());
                debugFrame.addText("SYSTEM:" + sb2.toString());
                IwidgetMethods.alert(result, sb2.toString());
                result = null;
            }
            return result;
        } else {
            debugFrame.addText("SYSTEM:Error:Widget " + widgetName + " does not contain widget.xml");
            return null;
        }
    }

    private WidgetElement constructWidget(String widgetName, String fileName) {
        WidgetElement result = new WidgetElement(this, widgetName);
        if (result.constructWidget(widgetName, "widget.xml")) {
            return result;
        } else {
            return null;
        }
    }

    // close a widget
    public void closeWidget(String title) {
        WidgetElement widget = (WidgetElement) widgetList.get(title);
        if (widget != null) {
            widget.close();
        }
        widgetList.remove(title);
        try {
            String encodedWidgetName = URLEncoder.encode(title, "UTF-8");
            openWidgets.put(encodedWidgetName, "0");
        } catch (Exception e) {
            debugFrame.addText(e.getMessage());
        }

        CheckboxMenuItem mi2 = (CheckboxMenuItem) widgetMenuList.get(title);
        if (mi2 != null) {
            mi2.setState(false);//mi2.setSelected(false);
        }
    }

    /**
     * onDrag widget event handler
     */
    private void handleWindowMove(int x, int y) {
        setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        int dX = x - mX;
        int dY = y - mY;
        if (dX != 0 || dY != 0) {
            int currentX = getX();
            int currentY = getY();
            setLocation(currentX + dX, currentY + dY);
        }
    }

    /**
     * iWidget exit
     */
    public void windowClosing(WindowEvent e) {
        saveConfiguration();
        for (Enumeration enum1 = widgetList.keys(); enum1.hasMoreElements(); closeWidget((String) enum1.nextElement()));
        //indicator.setVisible(false);
        //indicator.removeSystemTrayIconListener(this);
        trayicon.removeActionListener(this);
        systray.remove(trayicon);
        dispose();
        System.exit(0);
    }

    public void windowDeactivated(WindowEvent windowevent) {
    }

    public void windowActivated(WindowEvent windowevent) {
    }

    public void windowDeiconified(WindowEvent windowevent) {
    }

    public void windowIconified(WindowEvent windowevent) {
    }

    public void windowOpened(WindowEvent windowevent) {
    }

    public void windowClosed(WindowEvent windowevent) {
    }

    /**
     * mouse release, isdrag? or not
     */
    public void mouseReleased(MouseEvent e) {
        int button = e.getButton();
        if (osName.equals("Mac OS X")) {
            if ((e.getModifiers() & 2) == 0) {
                button = 1;
            } else {
                button = 3;
            }
        }
        if (button != 3) {
            isDragging = false;
            handleWindowMove(e.getX(), e.getY());
        }
    }

    /**
     * record mouse click position
     */
    public void mousePressed(MouseEvent e) {
        isDragging = true;
        mX = e.getX();
        mY = e.getY();
        setCursor(new Cursor(Cursor.HAND_CURSOR));
    }

    // mouse over
    public void mouseEntered(MouseEvent mouseevent) {
    }

    public void mouseExited(MouseEvent mouseevent) {
    }

    public void mouseClicked(MouseEvent mouseevent) {
    }

    private static Object deserialize(byte bytes[]) {
        try {
            Object result;
            ObjectInputStream inflator = new ObjectInputStream(new ByteArrayInputStream(bytes));
            result = inflator.readObject();
            inflator.close();
            return result;
        } catch (IOException e) {
            e.printStackTrace();
            throw new Error("ERROR:Shouldn't get an IOException here");
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
            throw new Error("ERROR:Can't find a class in an object that we serialized");
        }
    }

    /**
     * string replace tools
     */
    public static String replace(String input, String searchFor, String replaceWith, int startPos) {
        int start = input.indexOf(searchFor, startPos);
        if (start > -1) {
            int length = searchFor.length();
            int count = 0;
            int tokens[] = new int[1000];
            for (; start != -1; start = input.indexOf(searchFor, start + length)) {
                tokens[count++] = start;
            }

            StringBuffer sb = new StringBuffer(input);
            for (int iCtr = count - 1; iCtr >= 0; iCtr--) {
                int end = tokens[iCtr] + length;
                sb.replace(tokens[iCtr], end, replaceWith);
            }

            return sb.toString();
        } else {
            return input;
        }
    }

    public static String[] getCryptoImpls(String serviceType) {
        Set result = new HashSet();

        // All all providers
        Provider[] providers = Security.getProviders();
        for (int i = 0; i < providers.length; i++) {
            // Get services provided by each provider
            Set keys = providers[i].keySet();
            for (Iterator it = keys.iterator(); it.hasNext();) {
                String key = (String) it.next();
                key = key.split(" ")[0];
                if (key.startsWith(serviceType + ".")) {
                    result.add(key.substring(serviceType.length() + 1));
                } else if (key.startsWith("Alg.Alias." + serviceType + ".")) {
                    // This is an alias
                    result.add(key.substring(serviceType.length() + 11));
                }
            }
        }
        return (String[]) result.toArray(new String[result.size()]);
    }

    private void initSecurity() {
        try {
            String keyStr = "NotSoSecretKey";
            key = SecretKeyFactory.getInstance("DES").generateSecret(new DESKeySpec(keyStr.getBytes()));
            ecipher = Cipher.getInstance("DES");
            dcipher = Cipher.getInstance("DES");
            ecipher.init(1, key);
            dcipher.init(2, key);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String encrypt(String str) {
        try {
            String temp = str;
            while (temp.length() / 8 * 8 != temp.length()) {
                temp += " ";
            }
            // Encode the string into bytes using utf-8
            byte[] utf8 = temp.getBytes("UTF8");
            // Encrypt
            byte[] enc = ecipher.doFinal(utf8);
            // Encode bytes to base64 to get a string
            return new sun.misc.BASE64Encoder().encode(enc);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String str) {
        try {
            byte utf8[];
            byte dec[] = (new BASE64Decoder()).decodeBuffer(str);
            utf8 = dcipher.doFinal(dec);
            return (new String(utf8, "UTF8")).trim();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    private static final long serialVersionUID = 0x3836303032353334L;
    public static final int IWIDGET_VERSION_MAJOR = 1;
    public static final int IWIDGET_VERSION_MINOR = 0;
    public static final int IWIDGET_VERSION_BUILD = 0;
    private Iwidget thisInstance;
    public SystemTray systray;
    private TrayIcon trayicon;
    private PopupMenu menu;
    private Menu widgetMenu;
    private boolean isDragging;
    private int mX;
    private int mY;
    private java.util.Timer timer;
    private String widgetsDir[];
    private Hashtable widgetList;
    private Hashtable widgetMenuList;
    private DebugFrame debugFrame;
    private Manager manager;
    private Properties widgetVersions;
    private SecretKey key;
    private boolean foundError;
    private String javaVersion;
    public static String osName;
    public static IwidgetMouseLoop iwidgetMouse;
    private Properties openWidgets;
    private static Cipher ecipher;
    private static Cipher dcipher;
    byte salt[] = {
        -87, -101, -56, 50, 86, 53, -29, 3
    };
    int iterationCount;
}
